package ro.flashbite.sound
{
	import flash.display.MovieClip;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.media.SoundTransform;
	import flash.utils.Dictionary;
	import flash.utils.getTimer;
	
	import ro.flashbite.helpers.ArrayHelpers;
	import ro.flashbite.helpers.DictionaryHelpers;
	import ro.flashbite.helpers.MovieClipHelpers;
	import ro.flashbite.interfaces.IDestroyable;
	import ro.flashbite.loggers.ILogger;
	import ro.flashbite.loggers.LoggerFactory;
	
	//all data's are static, very usefull when setting global sound volume
	
	public final class SoundManager extends EventDispatcher implements IDestroyable
	{
		public static const SOUNDS_COMPLETE					:	String = "SOUNDS_COMPLETE";
		
		private static const DEFAULT_SOUND_TRANSFORM_VOLUME	:	Number = 1;
		private static const DEFAULT_PEAK_VALUE				:	Number = .5;
		
		private static var _moduleSounds					:	Dictionary; //holds all sounds in array based on moduleID given in registerSounds
		
		private static var _registeredSounds				:	Dictionary;
		
		private static var _logger							:	ILogger;
		
		private static var _currentlyPlayingSoundIDs		:	Array; //holds all names of the sounds from playing queue's
		
		private static var _voiceQueues						:	Vector.<SoundEnqueuer>;
		private static var _sfxQueues						:	Vector.<SoundEnqueuer>;
		private static var _ambianceQueues					:	Vector.<SoundEnqueuer>;
		
		private static var _registeredMouths				:	Dictionary;
		//will hold id and soundTransform value
		private static var _soundTransformDatabase			:	Dictionary;
		//this will hold the real soundTransform values
		private static var _realSoundTransformDatabase		:	Dictionary;
		
		private static var _peaksDatabase					:	Dictionary;
		
		private static var _globalVolume					:	Number = 1;
		
		// ================================================================================================================================
		// CONSTRUCTOR
		// ================================================================================================================================
		
		public function SoundManager(xmlSoundTransform:XML = null)
		{
			_constructor(xmlSoundTransform);
		}
		private function _constructor(xmlSoundTransform:XML):void
		{
			_soundTransformDatabase = new Dictionary(true);
			_realSoundTransformDatabase = new Dictionary(true);
			_peaksDatabase = new Dictionary(true);
			//read soundTransform
			if (xmlSoundTransform) {
				var xmlList:XMLList = xmlSoundTransform.soundTransform,
					xml:XML, 
					soundID:String,
					transVolume:Number,
					peakValue:Number;
				for each (xml in xmlList) {
					soundID = String(xml.@soundID);
					transVolume = Number(xml.@transform);
					peakValue = Number(xml.@peak);
					//saving
					_soundTransformDatabase[soundID] = new SoundTransform(transVolume);
					_realSoundTransformDatabase[soundID] = new SoundTransform(transVolume);
					_peaksDatabase[soundID] = peakValue;
				}
			}
			
			//internals
			_registeredSounds = new Dictionary(true);
			_logger = LoggerFactory.getLogger("SoundManager");
			
			_currentlyPlayingSoundIDs = [];
			
			_voiceQueues = new Vector.<SoundEnqueuer>();
			_sfxQueues = new Vector.<SoundEnqueuer>();
			_ambianceQueues = new Vector.<SoundEnqueuer>();
			
			_registeredMouths = new Dictionary(true);
			
			_moduleSounds = new Dictionary(true);
		}
		
		// ================================================================================================================================
		// PUBLIC STATIC
		// ================================================================================================================================
		
		public static function get GlobalVolume():Number { return _globalVolume; }
		
		public static function setGlobalVolume(newVolume:Number):void
		{
			/*var t:uint = getTimer();*/
			_globalVolume = newVolume;
			if (_soundTransformDatabase && _realSoundTransformDatabase && _registeredSounds) {
				//save in database the all new volume
				for (var sndID:String in _soundTransformDatabase) {
					if (_soundTransformDatabase[sndID] && _soundTransformDatabase[sndID] is SoundTransform) {
						var newSndTrans:SoundTransform = _soundTransformDatabase[sndID] as SoundTransform,
							newVolumeToApply:Number;
						//set the volume in database
						if (_globalVolume <= 0) newVolumeToApply = 0;
						else newVolumeToApply = newSndTrans.volume * _globalVolume;
						newSndTrans = new SoundTransform(newVolumeToApply);
						_realSoundTransformDatabase[sndID] = newSndTrans;
						//set the volume to sound
						if (_registeredSounds[sndID] && _registeredSounds[sndID] is Sound2) {
							var snd:Sound2 = _registeredSounds[sndID] as Sound2;
							snd.soundTransform = _realSoundTransformDatabase[sndID];
						}
					}
				}
			}
			/*t = getTimer() - t;*/
			/*if (_logger) _logger.debug("----------------Total time set globalVolume : " + t + " ms.");
			else trace("----------------[SoundManager] Total time set globalVolume : " + t + " ms.")*/
			
			_logger.debug("Sound global volume set to : " + newVolume);
		}
		
		/**
		 * Return the sound length in miliseconds of a registered sound, 0 if the sound isn't registered in database
		 */
		public static function getSoundLength(soundID:String):uint
		{
			if (_registeredSounds && _registeredSounds[soundID]) {
				return (_registeredSounds[soundID] as Sound2).getLength();
			}
			return 0;
		}
		
		
		// ================================================================================================================================
		
		// ================================================================================================================================
		// PUBLIC
		// ================================================================================================================================
		
		// --------------------------------------------------------------------------------------------------------------------------------
		// Pause-resume playing sounds
		public function sleep(moduleID:String = null):void
		{
			if (_currentlyPlayingSoundIDs) {
				for each (var sndID:String in _currentlyPlayingSoundIDs) {
					var snd:Sound2 = _registeredSounds[sndID],
						isInModule:Boolean = false;
					
					if (moduleID == null) isInModule = true;
					else {
						var arrModule:Array = _moduleSounds[moduleID] as Array;
						if (arrModule && arrModule.indexOf(snd.getId()) > -1) isInModule = true;
					}
					if (snd) {
						if (snd.isPlaying() && isInModule) snd.pause();
					}
				}
			}
		}
		
		public function wakeup(moduleID:String = null):void
		{
			if (_currentlyPlayingSoundIDs) {
				for each (var sndID:String in _currentlyPlayingSoundIDs) {
					var snd:Sound2 = _registeredSounds[sndID],
						isInModule:Boolean = false;
					
					if (moduleID == null) isInModule = true;
					else {
						var arrModule:Array = _moduleSounds[moduleID] as Array;
						if (arrModule && arrModule.indexOf(snd.getId()) > -1) isInModule = true;
					}
					if (snd) {
						if (snd.isPaused() && isInModule) snd.resume();
					}
				}
			}
		}
		// --------------------------------------------------------------------------------------------------------------------------------
		
		// --------------------------------------------------------------------------------------------------------------------------------
		// Sounds registrations/unregistration
		public function registerSound(sound:Sound2, moduleID:String):void
		{
			if (sound) {
				var sndId:String = sound.getId();
				
				if (!_registeredSounds[sndId]) {
					//set sound transform
					if (_realSoundTransformDatabase[sndId] == null) {
						//set defaults
						_realSoundTransformDatabase[sndId] = new SoundTransform(DEFAULT_SOUND_TRANSFORM_VOLUME * _globalVolume);
						_soundTransformDatabase[sndId] = new SoundTransform(DEFAULT_SOUND_TRANSFORM_VOLUME);
					}
					sound.soundTransform = _realSoundTransformDatabase[sndId];
					
					//set peak value
					if (_peaksDatabase[sndId] == null) {
						//defaults
						_peaksDatabase[sndId] = DEFAULT_PEAK_VALUE;
					}
					sound.peak = _peaksDatabase[sndId];
					//register
					_registeredSounds[sndId] = sound;
					
					//register in module
					if (_moduleSounds[moduleID] == null) {
						_moduleSounds[moduleID] = []; //array with name
					}
					(_moduleSounds[moduleID] as Array).push(sndId);
					
				} else _logger.error("This sound " + sndId + " is already registered");
			} else _logger.error("The sound provided is null");
		}
		
		public function unregisterSound(sound:Sound2, moduleID:String = null):void
		{
			if (sound) {
				unregisterSoundByID(sound.getId(), moduleID)
			} else _logger.error("The sound provided is null");
		}
		
		public function unregisterSoundByID(soundID:String, moduleID:String = null):void
		{
			if (_registeredSounds && _registeredSounds[soundID]) {
				//stop the sound
				stopSoundByID(soundID);
				//stop his enqueuers
				stopDefinedSound(_registeredSounds[soundID] as Sound2);
				//make also destroy on sound
				(_registeredSounds[soundID] as Sound2).destroy();
				_registeredSounds[soundID] = null;
				delete _registeredSounds[soundID];
				
				//unregister also from moduleSounds
				if (moduleID) {
					removeSoundFromModule(moduleID, soundID);
				} else {
					//search in the entire dictionary
					for each(var searchModuleID:String in _moduleSounds) {
						removeSoundFromModule(searchModuleID, soundID);
					}
				}
			} else _logger.error("This soundID " + soundID + " is not registered in database");
		}
		private function removeSoundFromModule(moduleID:String, soundID:String):void
		{
			var arr:Array = _moduleSounds[moduleID] as Array,
				index:int;
			if (arr) {
				index = arr.indexOf(soundID);
				if (index > -1) {
					(_moduleSounds[moduleID] as Array).splice(index, 1);
				} else {
					_logger.error("This soundID " + soundID + " is not registered in _moduleSounds[" + moduleID + "]");
				}
			}
		}
		
		public function unregisterAllSounds():void
		{
			//stop all sounds
			stopAllSounds();
			//make dictionary cleanup
			if (_registeredSounds) {
				for (var soundID:String in _registeredSounds) {
					unregisterSoundByID(soundID);
				}
			}
		}
		// --------------------------------------------------------------------------------------------------------------------------------
		
		// --------------------------------------------------------------------------------------------------------------------------------
		// Playing management
		public function play(soundIDs:Array, mouthID:String = null, loops:uint = 1, stopAlreadyPlayingSounds:Boolean = true, endFunction:Function = null):Boolean //returns the soundEnqueuer instance who manage this queue
		{	
			//check if all sounds are registered
			if (soundIDs && soundIDs.length > 0) {
				if (_registeredSounds == null) {
					_logger.error("Internal fatal error. _registeredSounds database is null");
					return false;
				}
				if (allSoundsAreRegistered(soundIDs) == false) {
					_logger.error("Not all the sounds provided are registered in database");
					return false;
				}
			} else {
				_logger.error("Sound list is null!!!");
				return false;
			}
			
			//check if all sounds are on the same channel
			if (allSoundsAreOnSameChannelID(soundIDs) == false) {
				_logger.error("SoundManager can't play a queue on different sound channels");
				return false;
			}
			
			//check if mouthID is registered
			if (_registeredMouths == null) {
				_logger.error("Internal fatal error. _registeredMouths database is null");
				return false;
			}
			//if mouthID is null, just continue....
			if (mouthID) {
				if (_registeredMouths[mouthID] == null) {
					_logger.error("This mouthID '" + mouthID + "' isn't registered in database");
				}
			}
			//check if the given queue is already playing -> if any of the given soundIDs are in _currentlyPlayingSounds
			if (_currentlyPlayingSoundIDs == null) {
				_logger.error("Internal fatal error. _currentlyPlayingSoundIDs array is null");
				return false;
			}
			var someSoundsAreAlreadyPlaying:Boolean = false;
			var overlappedSoundIDs:Array = [];
			for each (var soundID:String in soundIDs) {
				if (_currentlyPlayingSoundIDs.indexOf(soundID) > -1) {
					_logger.debug("Sound with id : " + soundID + " is already playing");
					someSoundsAreAlreadyPlaying = true;
					overlappedSoundIDs.push(soundID);
				}
			}
			
			//if the sounds defined in list are still playing, and the user wants to cancel them, stop.... also with the enqueuer.....
			if (someSoundsAreAlreadyPlaying ) {
				if (stopAlreadyPlayingSounds) {
					tryToStopDefinedSounds(overlappedSoundIDs);
				} else {
					//return, do not play the sounds?????
					return false;
				}
			}
			
			//check if the channel is free
			//on each channel, it will be max enqueuers definded in consts
			var channelToPlay:String = (_registeredSounds[soundIDs[0]] as Sound2).soundChannelID;
			var enqueuerToUse:SoundEnqueuer;
			var enqueueList:Vector.<SoundEnqueuer>;
			var enqueueListMaxLength:uint;
				//find the channel
			switch (channelToPlay) {
				case SoundChannelIds.VOICE:
					enqueueList = _voiceQueues;
					enqueueListMaxLength = SoundChannelIds.VOICE_MAX_CHANNELS;
					break;
				case SoundChannelIds.SFX:
					enqueueList = _sfxQueues;
					enqueueListMaxLength = SoundChannelIds.SFX_MAX_CHANNELS;
					break;
				case SoundChannelIds.AMBIANCE:
					enqueueList = _ambianceQueues;
					enqueueListMaxLength = SoundChannelIds.AMBIANCE_MAX_CHANNELS;
					//ambiance will play infinitely
					loops = int.MAX_VALUE;
					break;
				default:
					throw new Error("Channel unrecongized '" + channelToPlay + "'; Please use : " + SoundChannelIds.VOICE + ", " + SoundChannelIds.SFX  + " or " + SoundChannelIds.AMBIANCE);
					break;
			}
			
			if (enqueueList.length >= enqueueListMaxLength) {
				//max limit reached; check if some are still availables
				enqueuerToUse = getAvailableEnqueuer(enqueueList);
				if (enqueuerToUse == null) {
					_logger.debug("All enqueuers are used on this channel " + channelToPlay);
					return false;
				}
			} else {
				//create a new one
				enqueuerToUse = new SoundEnqueuer(channelToPlay + enqueueList.length);
				//saving type of channel
				enqueuerToUse.TYPE = channelToPlay;
				//save it
				enqueueList.push(enqueuerToUse);
			}
			
			//start the respective _queueManager
				//create the list
			var soundList:Vector.<Sound2> = new Vector.<Sound2>();
			for each (var sndID:String in soundIDs)
				soundList.push(_registeredSounds[sndID]);
			//mark them as playing
			for each (sndID in soundIDs)
				_currentlyPlayingSoundIDs.push(sndID);
				
			//some cleanup on _currentlyPlayingSoundIDs
			_currentlyPlayingSoundIDs = ArrayHelpers.removeDuplicates(_currentlyPlayingSoundIDs);
			
			//return true if the enqueuer starts
			//play	
			return enqueuerToUse.play(soundList, _registeredMouths[mouthID], mouthID, loops, onQueueComplete, endFunction);
		}
		
		public function stopSoundByID(soundID:String):void
		{
			if (soundIsRegistered(soundID)) {
				//stop also his queue
				if (soundIsPlaying(soundID)) {
					//find his channel
					var soundToStop:Sound2 = _registeredSounds[soundID] as Sound2,
						soundChannelID:String = soundToStop.soundChannelID;
					//find his channel queues
					var channelQueues:Vector.<SoundEnqueuer>;
					switch (soundChannelID) {
						case SoundChannelIds.VOICE:
							channelQueues = _voiceQueues;
							break;
						case SoundChannelIds.SFX:
							channelQueues = _sfxQueues;
							break;
						case SoundChannelIds.AMBIANCE:
							channelQueues = _ambianceQueues;
							break;
					}
					//find his soundEnqueuer manager
					var soundEnqueuer:SoundEnqueuer;
					if (channelQueues) {
						for each (var enq:SoundEnqueuer in channelQueues) {
							if (enq && enq.list && enq.list.indexOf(soundToStop) > -1 ) {
								//found
								soundEnqueuer = enq;
								break;
							}
						}
						if (soundEnqueuer == null) {
							_logger.debug("Couldn't find the sound in channelQueues");
							return;
						}
					} else {
						_logger.error("Couldn't find his channel queues");
					}
					
					//stop his soundEnqueuer manager
					if (soundEnqueuer) {
						//remove the enqueuer sounds list from _currentlyPlayingSounds
						if (soundEnqueuer.list && soundEnqueuer.list.length > 0) {
							for each (var sound:Sound2 in soundEnqueuer.list) {
								ArrayHelpers.removeValue(_currentlyPlayingSoundIDs, sound.getId());
							}
						}
						//enqueuer will stop the sounds
						soundEnqueuer.stop();
						soundEnqueuer.reset();
						
						//dispatch end complete sound on queue voice to stop avatar movement
						if (soundEnqueuer.TYPE == SoundChannelIds.VOICE)
							dispatchEvent(new Event(SOUNDS_COMPLETE));
					} else {
						_logger.error("Couldn't find his soundEnqueuer");
					}
				} else {
					ArrayHelpers.removeValue(_currentlyPlayingSoundIDs, soundID);
				}
			} else _logger.error("This soundID " + soundID + " isn't registered in database");
		}
		
		public function stopAllSounds():void
		{
			//stop active queue's
			stopEnqueuers(_voiceQueues);
			stopEnqueuers(_sfxQueues);
			stopEnqueuers(_ambianceQueues);
		}
		// --------------------------------------------------------------------------------------------------------------------------------
		
		// --------------------------------------------------------------------------------------------------------------------------------
		// Speech sync
		public function registerMouth(mcMouth:MovieClip, mouthID:String, overwrite:Boolean = true):void
		{
			//check if is a known mouthID
			if (CharactersMouthIDs.ALL_MOUTH_IDS.indexOf(mouthID) > -1) {
				if (_registeredMouths) {
					if (_registeredMouths[mouthID]) {
						if (overwrite) {
							unregisterMouth(mouthID);
							MovieClipHelpers.gotoAndStopRecursive(mcMouth);
							_registeredMouths[mouthID] = mcMouth;
							//set also on speechManagers
							setMouthOnActiveVoiceQueues(mcMouth, mouthID);
							_logger.debug("This mouthID " + mouthID + " is already registered and it has been overriden");
						} else _logger.debug("This mouthID " + mouthID + " is already registered and it hasn't been overriden");
					} else {
						MovieClipHelpers.gotoAndStopRecursive(mcMouth);
						_registeredMouths[mouthID] = mcMouth;
						//set also on speechManagers
						setMouthOnActiveVoiceQueues(mcMouth, mouthID);
						_logger.debug("Mouth id : " + mouthID + " registered");
					}
				} else _logger.error("Registered mouths database is null");
			} else {
				_logger.error("You tried to register a mouth that isn't available");
			}
		}
		private function setMouthOnActiveVoiceQueues(mcMouth:MovieClip, mouthID:String):void
		{
			if (_voiceQueues && _voiceQueues.length) {
				for each (var enqueuer:SoundEnqueuer in _voiceQueues) {
					if (enqueuer.queueIsPlaying && enqueuer.speechManagerMouthID == mouthID) {
						enqueuer.setSpeechManagerNewMouth(mcMouth, mouthID);
						break;
					}
				}
			}
		}
		
		public function unregisterMouth(mouthID:String):void
		{
			//TODO: stop voice queues...
			if (_registeredMouths) {
				if (_registeredMouths[mouthID]) {
					_registeredMouths[mouthID] = null;
					delete _registeredMouths[mouthID];
				}
			}
		}
		// --------------------------------------------------------------------------------------------------------------------------------
		
		public function soundIsPlaying(soundID:String):Boolean
		{
			if (soundID) {
				if (_currentlyPlayingSoundIDs && _currentlyPlayingSoundIDs.indexOf(soundID) > -1) return true;
				return false;
			} else {
				throw new Error("You tried to search for a null soundID");
				return false;
			}
		}
		
		// ================================================================================================================================
		// PRIVATE
		// ================================================================================================================================
		
		private function onQueueComplete(enqueuer:SoundEnqueuer):void
		{
			//remove from playing sounds the current from this complete enqueuer
			for each (var sound:Sound2 in enqueuer.list) {
				var indx:int = _currentlyPlayingSoundIDs.indexOf(sound.getId());
				if (indx > -1) 
					_currentlyPlayingSoundIDs.splice(indx, 1);
			}
			
			enqueuer.reset();
			
			//dispatch end complete sound on queue voice to stop avatar movement
			if (enqueuer.TYPE == SoundChannelIds.VOICE)
				dispatchEvent(new Event(SOUNDS_COMPLETE));
		}
		
		private function getAvailableEnqueuer(enqueueList:Vector.<SoundEnqueuer>):SoundEnqueuer
		{
			for each (var enq:SoundEnqueuer in enqueueList) {
				if (enq && enq.isAvailable) {
					return enq;
				}
			}
			
			return null;
		}
		
		private function allSoundsAreRegistered(soundIDs:Array):Boolean
		{
			var allSoundsAreRegistered:Boolean = true;
			for each (var soundID:String in soundIDs) {
				if (_registeredSounds[soundID] == null) {
					_logger.error("Sound with id '" + soundID + "' isn't registered");
					allSoundsAreRegistered = false;
				}
			}
			
			return allSoundsAreRegistered;
		}
		
		private function allSoundsAreOnSameChannelID(soundIDs:Array):Boolean
		{
			//get the channelID from first sound
			var currentChannelID:String = (_registeredSounds[soundIDs[0]] as Sound2).soundChannelID;
			var allSoundsAreOnSameChannelID:Boolean = true;
			for each (var soundID:String in soundIDs) {
				if (currentChannelID != (_registeredSounds[soundID] as Sound2).soundChannelID) {
					_logger.error("Sound with id '" + soundID + "' isn't on the same channel; channel = " + currentChannelID);
					allSoundsAreOnSameChannelID = false;
				}
			}
			return allSoundsAreOnSameChannelID;
		}
		
		private function soundIsRegistered(soundID:String):Boolean
		{
			if (_registeredSounds) {
				if (_registeredSounds[soundID] != null) return true;
				return false;
			}
			
			return false;
		}
		
		private function stopEnqueuers(vectorQueue:Vector.<SoundEnqueuer>):void
		{
			if (vectorQueue) {
				if (vectorQueue.length > 0) {
					for each (var enqueuer:SoundEnqueuer in vectorQueue) {
						//enqueuer will stop the sounds
						if (enqueuer) {
							if (enqueuer.list && enqueuer.list.length) {
								for each (var snd:Sound2 in enqueuer.list) {
									stopSoundByID(snd.getId());
								}
							}
							enqueuer.stop();
						}
					}
				}
			}
		}
		
		private function destroyEnqueuers(vectorQueue:Vector.<SoundEnqueuer>):void
		{
			if (vectorQueue) {
				if (vectorQueue.length > 0) {
					for each (var enqueuer:SoundEnqueuer in vectorQueue) {
						//enqueuer destroy
						if (enqueuer) enqueuer.destroy();
					}
				}
			}
		}
		
		private function tryToStopDefinedSounds(soundToStopIDs:Array):void
		{
			_logger.debug("Trying to stop enqueuers that have currentSounds : " + soundToStopIDs);
			
			for each (var sndID:String in soundToStopIDs) {
				stopDefinedSound(_registeredSounds[sndID] as Sound2);
			}
		}
		private function stopDefinedSound(sound:Sound2):void
		{
			if (sound && sound.getId())
				stopSoundByID(sound.getId());
			
			/*var channel:String = sound.soundChannelID,
				queueToSearch:Vector.<SoundEnqueuer>;
			switch (channel) {
				case SoundChannelIds.VOICE:
					queueToSearch = _voiceQueues;
					break
				case SoundChannelIds.SFX:
					queueToSearch = _sfxQueues;
					break;
				case SoundChannelIds.AMBIANCE:
					queueToSearch = _ambianceQueues;
					break;
			}
			
			if (queueToSearch) {
				//real stop of enqueuer
				for each (var enqueuer:SoundEnqueuer in queueToSearch) {
					if (enqueuer && enqueuer.isPlayingSound(sound)) {
						enqueuer.stop();
						enqueuer.reset();
					}
				}
			} else _logger.error("Couldn't find the queue of sound channel");*/
		}
		
		// ================================================================================================================================
		// DESTRUCTOR
		// ================================================================================================================================
		
		public function destroy():void
		{
			if (_moduleSounds) {
				DictionaryHelpers.clear(_moduleSounds);
				_moduleSounds = null;
			}
			
			if (_voiceQueues) {
				destroyEnqueuers(_voiceQueues);
				_voiceQueues = null;
			}
			if (_sfxQueues) {
				destroyEnqueuers(_sfxQueues);
				_sfxQueues = null;
			}
			if (_sfxQueues) {
				destroyEnqueuers(_sfxQueues);
				_sfxQueues = null;
			}
			
			if (_registeredMouths) {
				for (var mouthID:String in _registeredMouths) {
					_registeredMouths[mouthID] = null;
					delete _registeredMouths[mouthID];
				}
				_registeredMouths = null;
			}
			
			unregisterAllSounds();
			
			if (_soundTransformDatabase) {
				DictionaryHelpers.clear(_soundTransformDatabase);
				_soundTransformDatabase = null;
			}
			if (_realSoundTransformDatabase) {
				DictionaryHelpers.clear(_realSoundTransformDatabase);
				_soundTransformDatabase = null;
			}
			if (_peaksDatabase) {
				DictionaryHelpers.clear(_peaksDatabase);
				_peaksDatabase = null;
			}
			
			_registeredSounds = null;
			_logger = null;
			_currentlyPlayingSoundIDs = null;
		}
	}
}